package profit

import (
	"context"
	"errors"
	"fmt"
	"git.mycaigou.com/gfyx/common/authorize"
	"git.mycaigou.com/gfyx/common/cache"
	"git.mycaigou.com/gfyx/common/tenant_db"
	"git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/common/constants"
	"git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/common/utils"
	"git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/repository/base/area"
	profitRepo "git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/repository/base/profit"
	"git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/repository/remote/gfyx_user_service_remote"
	"git.mycaigou.com/gfyx/micro-gfyx-oppty-service/infrastructure/repository/remote/ycg_remote"
	basePb "git.mycaigou.com/gfyx/micro-gfyx-oppty-service/proto/micro_gfyx_oppty_service_proto/micro-gfyx-oppty-service/base"
	tenantPb "git.mycaigou.com/gfyx/micro-gfyx-oppty-service/proto/micro_gfyx_user_service_proto/micro-gfyx-user-service/tenant"
	"git.mycaigou.com/gfyx/micro-gfyx-oppty-service/service/base/user"
	"git.myscrm.cn/golang/stark/v4"
	"github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"
	"google.golang.org/grpc"
	"strings"
	"time"
)

type profitService struct {
	profitRepo          profitRepo.ProfitRepositoryIface
	userService         user.UserServiceIface
	ycgRemoteRepo       ycg_remote.YcgRepositoryIface
	areaConfigRepo      area.AreaConfigRepositoryIface
	gfyxUserServiceRepo gfyx_user_service_remote.GfyxUserServiceRepositoryIface
}

// NewProfitService 构造函数
func NewProfitService(profitRepo profitRepo.ProfitRepositoryIface,
	userService user.UserServiceIface,
	ycgRemoteRepo ycg_remote.YcgRepositoryIface,
	areaConfigRepo area.AreaConfigRepositoryIface,
	gfyxUserServiceRepo gfyx_user_service_remote.GfyxUserServiceRepositoryIface,
) ProfitServiceIface {
	return &profitService{
		profitRepo:          profitRepo,
		userService:         userService,
		ycgRemoteRepo:       ycgRemoteRepo,
		areaConfigRepo:      areaConfigRepo,
		gfyxUserServiceRepo: gfyxUserServiceRepo,
	}
}

// GetTenantProfit 获取租户的权益期内的模块，系统整个权益模块目前分为 市场洞察和线索雷达
func (s *profitService) GetTenantProfit(ctx context.Context) ([]string, error) {
	profits := make([]string, 0)
	list, err := s.profitRepo.QueryAllEffectiveProfitList(ctx)
	if err != nil {
		stark.Logger.Errorf(ctx, "GetTenantProfit QueryAllEffectiveProfitList,err:%s", err.Error())
		return profits, err
	}
	for _, profitItem := range list {
		if profitItem.ProfitCode == constants.ProfitClueRadarNationwide ||
			profitItem.ProfitCode == constants.ProfitMarketReport ||
			profitItem.ProfitCode == constants.ProfitClueRadarProvince ||
			profitItem.ProfitCode == constants.ProfitAIOutbound {
			profits = append(profits, profitItem.ProfitCode)
		}
	}
	return profits, nil
}

// GetTenantProfitList 获取租户权益列表
func (s *profitService) GetTenantProfitList(ctx context.Context) ([]*profitRepo.GfyxProfitPo, error) {
	list, err := s.profitRepo.QueryAllEffectiveProfitList(ctx)
	if err != nil {
		stark.Logger.Errorf(ctx, "GetTenantProfitList QueryAllEffectiveProfitList,err:%s", err.Error())
		return nil, err
	}
	return list, nil
}

func (s *profitService) CalculateProfitAccountNum(ctx context.Context) error {
	tenantList, err := s.gfyxUserServiceRepo.GetAllEfficientTenant(ctx, &tenantPb.GetAllEfficientTenantRequest{})
	//for test
	//tenantList = []*tenant_common.Tenant{{Code: "ompycga"}}
	//for test end
	stark.Logger.Infof(ctx, "CalculateProfitAccountNum tenantList:%+v", tenantList)

	if err != nil {
		return err
	}
	for _, tenantInfo := range tenantList.List {
		ctx, _ = tenant_db.GetTenantBackgroundContext(ctx, tenantInfo.Code)
		if (&tenant_db.TenantDB{}).GetTenantDB(ctx) == nil {
			stark.Logger.Errorf(ctx, "CalculateProfitAccountNum tenantCode:%s,GetTenantDB failed", tenantInfo.Code)
			continue
		}
		//ctx = context.WithValue(ctx, authorize.TENANT_CODE_KEY, tenantInfo.Code)
		err = s.calculateTenantProfitAccountNum(ctx, tenantInfo.Code)
		if err != nil {
			stark.Logger.Errorf(ctx, "CalculateProfitAccountNum tenantCode:%s, err:%s", tenantInfo.Code, err.Error())
			continue
		} else {
			stark.Logger.Infof(ctx, "CalculateProfitAccountNum tenantCode:%s, succeed", tenantInfo.Code)
		}
	}

	return nil
}

func (s *profitService) getYcgMemberProfit(ctx context.Context, tenantCode string) error {
	tenantInfo, _ := s.gfyxUserServiceRepo.GetTenantByCode(ctx, &tenantPb.GetTenantByCodeRequest{Code: tenantCode})
	if tenantInfo == nil || tenantInfo.Name == "" {
		return nil
	}

	ycgMemberInfo, err := s.ycgRemoteRepo.GetSaasSupplierMemberInfo(ctx, tenantInfo.Name)
	if ycgMemberInfo == nil {
		return nil
	}
	ycgMemberProfit := &profitRepo.GfyxProfitPo{
		ProfitCode: constants.ProfitYcgMemberAccount,
		StartTime:  time.Now(),
		Number:     ycgMemberInfo.AccountCount,
		CreatedOn:  time.Now(),
		CreatedBy:  "定时任务",
		ModifiedOn: time.Now(),
		ModifiedBy: "定时任务",
		IsDeleted:  0,
	}
	if ycgMemberInfo.SupplierLevel > 0 && ycgMemberInfo.EndAt != "" {
		ycgMemberProfit.EndTime, err = time.ParseInLocation("2006-01-02 15:04:05", ycgMemberInfo.EndAt, time.Local)
		if err != nil {
			return err
		}
		if ycgMemberProfit.EndTime.Before(time.Now()) {
			// 云采购的会员已过期，不再计算
			return s.profitRepo.UpdateProfit(ctx, map[string]interface{}{"profit_code": constants.ProfitYcgMemberAccount},
				map[string]interface{}{"is_deleted": constants.IsDeletedTrue})
		}

		if ycgMemberInfo.SupplierLevel == 6 {
			//平台会员专业版
			ycgMemberProfit.Number = 20
		}
	} else {
		//无平台会员
		return s.profitRepo.UpdateProfit(ctx, map[string]interface{}{"profit_code": constants.ProfitYcgMemberAccount},
			map[string]interface{}{"is_deleted": constants.IsDeletedTrue})
	}

	_, existYcgMemberProfitList, err := s.profitRepo.QueryEffectiveProfitListByCondition(ctx,
		map[string]interface{}{"profit_code": constants.ProfitYcgMemberAccount, "is_deleted": constants.IsDeletedFalse}, nil)
	if err != nil {
		return err
	}

	if len(existYcgMemberProfitList) == 0 {
		_, err = s.profitRepo.CreateProfit(ctx, ycgMemberProfit)
		if err != nil {
			return err
		}
	} else {
		if len(existYcgMemberProfitList) > 1 {
			//如果有多条记录，删除第二至最后一条
			for i, po := range existYcgMemberProfitList {
				if i == 0 {
					continue
				}
				_ = s.profitRepo.UpdateProfit(ctx, map[string]interface{}{"id": po.Id},
					map[string]interface{}{"is_deleted": constants.IsDeletedTrue})
			}
		}
		existYcgMemberProfit := existYcgMemberProfitList[0]
		if !existYcgMemberProfit.EndTime.Equal(ycgMemberProfit.EndTime) ||
			ycgMemberProfit.Number != existYcgMemberProfit.Number {
			_ = s.profitRepo.UpdateProfit(ctx, map[string]interface{}{"id": existYcgMemberProfit.Id},
				map[string]interface{}{"number": ycgMemberProfit.Number, "end_time": ycgMemberProfit.EndTime})
		}
	}
	return nil
}

func (s *profitService) calculateTenantProfitAccountNum(ctx context.Context, tenantCode string) error {
	//1.使用租户名称，调用云采购平台接口获取会员信息，保存至权益表中
	err := s.getYcgMemberProfit(ctx, tenantCode)
	if err != nil {
		stark.Logger.Infof(ctx, "getYcgMemberProfit tenantCode:%s, err:%s", tenantCode, err.Error())
	}

	//2.获取有效期内的权益记录，计算可用的账号数量
	profitList, err := s.profitRepo.QueryAllEffectiveProfitList(ctx)
	if err != nil {
		return err
	}

	//线索雷达省版——首个省5个账户，非首个2个账户
	//省市雷达全国——20个账户
	//市场洞察——5个账户
	//平台基础版——5个账户
	//平台专业版——20个账户
	//账户数——按照购买数量
	//以上产品的账户相加，等于用户实际可用账户数
	var accountNum int32 = 1 //初始值等于1，是默认有1个管理员账号
	isFirstProvince := true
	for _, profitPo := range profitList {
		switch profitPo.ProfitCode {
		case constants.ProfitMarketReport:
			accountNum += profitPo.Number * 5
		case constants.ProfitClueRadarNationwide:
			accountNum += profitPo.Number * 20
		case constants.ProfitClueRadarProvince:
			if isFirstProvince {
				accountNum += 3
				isFirstProvince = false
			}
			accountNum += profitPo.Number * 2
		case constants.ProfitYcgMemberAccount:
			accountNum += profitPo.Number
		case constants.ProfitUserAccount:
			accountNum += profitPo.Number
		}
	}

	//3.将账号数量保存至权益账号数量表中，当账号数量发生变化时，冻结或解冻对应用户
	profitAccountPo, err := s.profitRepo.QueryProfitAccount(ctx)
	if err != nil {
		return err
	}
	if profitAccountPo == nil {
		//无记录，新增记录
		_, err = s.profitRepo.CreateProfitAccount(ctx, &profitRepo.GfyxProfitAccountPo{
			Account:    accountNum,
			CreatedOn:  time.Now(),
			CreatedBy:  "定时任务",
			ModifiedOn: time.Now(),
			ModifiedBy: "定时任务",
			IsDeleted:  0,
		})
		if err != nil {
			return err
		}
		if false {
			err = s.userService.ChangeUserStatusByProfitAccountNum(ctx, accountNum)
			if err != nil {
				return err
			}
		}
	} else {
		// 有记录，比对账号数量是否发生变化
		if profitAccountPo.Account != accountNum {
			err = s.profitRepo.UpdateProfitAccount(ctx, map[string]interface{}{"is_deleted": constants.IsDeletedFalse},
				map[string]interface{}{"is_deleted": constants.IsDeletedTrue})
			if err != nil {
				return err
			}
			_, err = s.profitRepo.CreateProfitAccount(ctx, &profitRepo.GfyxProfitAccountPo{
				Account:    accountNum,
				CreatedOn:  time.Now(),
				CreatedBy:  "定时任务",
				ModifiedOn: time.Now(),
				ModifiedBy: "定时任务",
				IsDeleted:  0,
			})
			if err != nil {
				return err
			}

			// 禁用超出授权账号数量的账号功能，暂时不启用
			if false {
				tenantWhiteConfig := stark.Configer.(stark.ApolloConfigerIface).GetStringValue("without_forbid_user_tenant_list", "")
				tenantWhiteList := strings.Split(tenantWhiteConfig, ",")
				isWhile := false
				for _, tenantCodeItem := range tenantWhiteList {
					if tenantCodeItem == tenantCode {
						isWhile = true
						break
					}
				}

				if !isWhile {
					err = s.userService.ChangeUserStatusByProfitAccountNum(ctx, accountNum)
					if err != nil {
						return err
					}
				}
			}

		}
	}

	return nil
}

// SyncProfit 建模出库时调用该方法同步旧表的权益到新的权益表
func (s *profitService) SyncProfit(ctx context.Context, profit *profitRepo.GfyxProfitPo) error {
	count, _, err := s.profitRepo.QueryEffectiveProfitListByCondition(ctx, map[string]interface{}{"profit_guid": profit.ProfitGuid}, nil)
	if err != nil {
		stark.Logger.Errorf(ctx, "SyncProfit QueryEffectiveProfitListByCondition err:%s", err.Error())
	}
	if count == 0 {
		//新增
		_, err = s.profitRepo.CreateProfit(ctx, profit)
		if err != nil {
			stark.Logger.Errorf(ctx, "SyncProfit CreateProfit err:%s", err.Error())
		}
	} else {
		//更新
		err = s.profitRepo.UpdateProfit(ctx, map[string]interface{}{"profit_guid": profit.ProfitGuid},
			map[string]interface{}{
				"start_time":  profit.StartTime,
				"end_time":    profit.EndTime,
				"number":      profit.Number,
				"profit_code": profit.ProfitCode,
				"modified_on": time.Now(),
				"modified_by": "sync",
			})
		if err != nil {
			stark.Logger.Errorf(ctx, "SyncProfit UpdateProfit err:%s", err.Error())
		}
	}

	// 计算账号数量

	go func() {
		err = s.CalculateProfitAccountNum(context.Background())
		if err != nil {
			stark.Logger.Errorf(ctx, "SyncProfit CalculateProfitAccountNum err:%s", err.Error())
		}
	}()

	return nil
}

func (s *profitService) SaveTenantProfit(ctx context.Context, request *basePb.SaveTenantProfitRequest) (*basePb.SaveTenantProfitResponse, error) {
	res := &basePb.SaveTenantProfitResponse{}
	for _, item := range request.ProfitList {
		exist, err := s.profitRepo.ExistProfit(ctx, item.DeliveryNo, item.DeliveryBatchNo, item.DeliveryAuthId)
		if err != nil {
			stark.Logger.Errorf(ctx, "ExistProfit err:%s", err.Error())
		}
		if exist.Id == 0 {
			data := &profitRepo.GfyxProfitPo{
				ProfitCode:      item.ProfitCode,
				StartTime:       utils.StringToSqlTime(item.StartTime).Time,
				EndTime:         utils.StringToSqlTime(item.EndTime).Time,
				Number:          item.Number,
				DeliveryNo:      item.DeliveryNo,
				DeliveryBatchNo: item.DeliveryBatchNo,
				DeliveryAuthId:  item.DeliveryAuthId,
				CreatedBy:       item.CreatedBy,
				CreatedOn:       time.Now(),
				ModifiedOn:      time.Now(),
			}
			_, err = s.profitRepo.CreateProfit(ctx, data)
			if err != nil {
				stark.Logger.Errorf(ctx, "ExistProfit err:%s", err.Error())
			}
		}
	}
	res.IsSuccess = true
	return res, nil

}

// GetProfitProvince 获取（省份）数据权限
func (s *profitService) GetProfitProvince(ctx context.Context) ([]string, error) {
	provinceCode := make([]string, 0)
	list, err := s.profitRepo.QueryProfitProvinceList(ctx)
	if err != nil {
		return provinceCode, err
	}
	if list != nil {
		for _, item := range list {
			provinceCode = append(provinceCode, item.ProvinceCode)
		}
	}
	return provinceCode, nil
}

// SaveProfitProvince 保存省份数据权益
func (s *profitService) SaveProfitProvince(ctx context.Context, provinceCode []string) error {
	// 线索雷达-省版 权益数
	var profitProvinceAmount int

	// 检查设置省份数量
	profitList, err := s.profitRepo.QueryAllEffectiveProfitList(ctx)
	if err != nil {
		return err
	}
	for _, profit := range profitList {
		if profit.ProfitCode == constants.ProfitClueRadarProvince {
			profitProvinceAmount = profitProvinceAmount + int(profit.Number)
		} else if profit.ProfitCode == constants.ProfitClueRadarNationwide && len(provinceCode) > 0 {
			return fmt.Errorf("线索雷达-全国版不支持设置省份")
		}
	}
	if len(provinceCode) > profitProvinceAmount {
		return fmt.Errorf("最多可选择%d个省份", profitProvinceAmount)
	}

	// 检查省份code
	if len(provinceCode) > 0 {
		areaList, err := s.areaConfigRepo.AreaConfigList(ctx)
		if err != nil {
			return err
		}
		for _, code := range provinceCode {
			var isMatch bool
			for _, item := range areaList {
				if code == item.ItemCode {
					isMatch = true
					break
				}
			}
			if !isMatch {
				return errors.New("省份不存在")
			}
		}
	}

	// 保存省份数据权益
	if err := s.profitRepo.SaveProfitProvince(ctx, provinceCode); err != nil {
		return err
	}
	return nil
}

// CalculateTenantProfitExportNum 计算租户可用导出数据量,和租期的开始结束时间
func (s *profitService) CalculateTenantProfitExportNum(ctx context.Context) (num int32, st time.Time, et time.Time, err error) {
	var profitPoList []*profitRepo.GfyxProfitPo
	profitPoList, err = s.profitRepo.QueryAllEffectiveProfitList(ctx)
	if err != nil {
		return
	}

	if len(profitPoList) == 0 {
		return
	}

	for _, profitPo := range profitPoList {
		if profitPo.ProfitCode == constants.ProfitClueRadarNationwide ||
			profitPo.ProfitCode == constants.ProfitClueRadarProvince {
			if st.IsZero() || profitPo.StartTime.Before(st) {
				st = profitPo.StartTime
			}
			if et.IsZero() || profitPo.EndTime.After(et) {
				et = profitPo.EndTime
			}

		}
		years := profitPo.EndTime.Year() - profitPo.StartTime.Year()
		if years == 0 {
			years = 1
		}
		switch profitPo.ProfitCode {
		case constants.ProfitClueRadarNationwide, constants.ProfitMarketReport:
			num += int32(years) * 100000
		case constants.ProfitClueRadarProvince:
			num += int32(years) * profitPo.Number * 5000
		case constants.ProfitExportNum:
			num += profitPo.Number
		}

	}

	return
}

func checkTenantProfitEffective(ctx context.Context) (bool, error) {
	cacheRedis, _ := cache.TenantRedis()
	cacheKey := "profit:profit_effective" //当前租户在有效期的权益

	cacheResult, err := cacheRedis.Get(ctx, cacheKey)
	if err != nil {
		return false, err
	}

	if cacheResult != "" &&
		(cacheResult == constants.ProfitClueRadarNationwide ||
			cacheResult == constants.ProfitMarketReport ||
			cacheResult == constants.ProfitClueRadarProvince ||
			cacheResult == constants.ProfitAIOutbound) {
		return true, nil
	}

	profitRepository := profitRepo.NewProfitRepository()
	profitList, err := profitRepository.QueryAllEffectiveProfitList(ctx)
	if err != nil {
		return false, err
	}
	for _, profit := range profitList {
		//有AI外呼、市场洞察、线索雷达省版或全国版的任一未过期权益
		if profit.ProfitCode == constants.ProfitClueRadarNationwide ||
			profit.ProfitCode == constants.ProfitMarketReport ||
			profit.ProfitCode == constants.ProfitClueRadarProvince || profit.ProfitCode == constants.ProfitAIOutbound {
			//租户权益未过期，缓存至当天的最后一刻
			todayLastTime, err := time.ParseInLocation("2006-01-02 15:04:05", time.Now().Format("2006-01-02")+" 23:59:59", time.Local)
			cacheTimeOut := 3600
			if err == nil {
				cacheTimeOut = int(todayLastTime.Sub(time.Now()).Seconds())
				if cacheTimeOut <= 0 || cacheTimeOut >= 24*60*60 {
					//避免计算错误
					cacheTimeOut = 3600
				}
			}
			_, _ = cacheRedis.Set(ctx, cacheKey, profit.ProfitCode, true, cacheTimeOut)
			return true, nil
		}
	}

	return false, nil
}

func CheckTenantProfitEffective(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
	ignoreProfit := metautils.ExtractIncoming(ctx).Get(authorize.IgnoreProfit)
	//ignoreProfit=1,忽略权益校验
	if ignoreProfit == "1" {
		return handler(ctx, req)
	}
	//校验权益
	isEffective, err := checkTenantProfitEffective(ctx)
	if err != nil {
		return nil, err
	}
	if !isEffective {
		return nil, errors.New("租户无权益或权益已过期")
	}
	return handler(ctx, req)
}
